Here we will map an example dataset from three leukemias sequenced in our study. This entire tutorial should take less than 10 minutes to run

Setup

library(Seurat)
library(tidyverse)
library(symphony)
library(ggpubr)
library(patchwork)
library(RColorBrewer)
Warning: package ‘RColorBrewer’ was built under R version 4.1.2

Install package from github

## install dependencies that are not on CRAN
if(!require(BiocManager, quietly = TRUE)) install.packages("BiocManager")
BiocManager::install(c("AUCell", "doMC", "BiocNeighbors"))
if(!require(devtools, quietly = TRUE)) install.packages("devtools")
devtools::install_github("jaredhuling/jcolors")
## install BoneMarrowMap package
devtools::install_github('andygxzeng/BoneMarrowMap', force = TRUE)
library(BoneMarrowMap)

Download reference object and UMAP model

Set projection folder, Download reference object and UMAP model

# Set directory to store projection reference files
projection_path = './'

# Download Bone Marrow Reference - 344 Mb
curl::curl_download('https://bonemarrowmap.s3.us-east-2.amazonaws.com/BoneMarrowMap_SymphonyReference.rds', 
                    destfile = paste0(projection_path, 'BoneMarrowMap_SymphonyReference.rds'))
# Download uwot model file - 221 Mb
curl::curl_download('https://bonemarrowmap.s3.us-east-2.amazonaws.com/BoneMarrowMap_uwot_model.uwot', 
                    destfile = paste0(projection_path, 'BoneMarrowMap_uwot_model.uwot'))
projection_path = './'
# Load Symphony reference
ref <- readRDS(paste0(projection_path, 'BoneMarrowMap_SymphonyReference.rds'))
# Set uwot path for UMAP projection
ref$save_uwot_path <- paste0(projection_path, 'BoneMarrowMap_uwot_model.uwot')

Visualize Bone Marrow Reference

If we want to visualize celltype labels or metadata from the BM Reference, we can create a Seurat Object from the symphony reference This will be memory efficient as it will not include gene expression counts, only the UMAP coordinates and the metadata including cell labels and sorting information

ReferenceSeuratObj <- create_ReferenceObject(ref)

DimPlot(ReferenceSeuratObj, reduction = 'umap', group.by = 'CellType_Annotation_formatted', 
        raster=FALSE, label=TRUE, label.size = 4)

We can also visualize broader cell type labels which may simplify interpretation and downstream analysis.

DimPlot(ReferenceSeuratObj, reduction = 'umap', group.by = 'CellType_Broad', 
        raster=FALSE, label=TRUE, label.size = 4)

We can visualize other annotations too, including cell cycle phase and lineage pseudotime estimates.

p1 <- DimPlot(ReferenceSeuratObj, reduction = 'umap', group.by = 'CyclePhase', raster=FALSE)
p2 <- FeaturePlot(ReferenceSeuratObj, reduction = 'umap', features = 'Pseudotime', raster=FALSE) 

p1 + p2

Load Leukemia scRNA-seq data.

We have confidently mapped leukemias spanning AML (incl. AMKL and AEL), B-ALL, MPAL, MDS, CML, MPN, and BDPCN, across sequencing technologies. However, we cannot be confident in mapping T-ALL due to a lack of thymus-specific reference data on T cell precursor stages. Further, our tool does not discriminate between normal vs malignant cells, which is an important consideration particularly within low-blast count chronic diseases.

As an example here, we are going to project scRNA-seq data from three diverse AML patients sequenced in our study.

  • pt_17844: MLL-AF9 translocation with a purely mature leukemia cell hierarchy
  • pt_17746: NPM1c + DNMT3A + TET2 with a GMP-dominant leukemia cell hierarchy
  • pt_30886: Complex Karyotype with a primitive leukemia cell hierarchy + extensive erythroid involvement

For convenience, each patient sample was downsampled to 2500 cells.

curl::curl_download('https://bonemarrowmap.s3.us-east-2.amazonaws.com/ExampleQuery_AML_scRNAseq.rds', 
                    destfile = paste0(projection_path, 'ExampleQuery_AML_scRNAseq.rds'))
query <- readRDS('ExampleQuery_AML_scRNAseq.rds')
query
An object of class Seurat 
36601 features across 7500 samples within 1 assay 
Active assay: RNA (36601 features, 0 variable features)

Map the Query Data

Provide raw counts, metadata, and donor key. This should take <1 min Calculate mapping error and perform QC to remove low quality cells with high mapping error

# batch variable to correct in the query data, set as NULL if no batches in query
batchvar <- 'Patient'

# Map query dataset using Symphony (Kang et al 2021)
query <- map_Query(
    exp_query = query[['RNA']]@counts, 
    metadata_query = query@meta.data,
    ref_obj = ref,
    vars = batchvar
)
Normalizing
Scaling and synchronizing query gene expression
Found 2360 reference variable genes in query dataset
Project query cells using reference gene loadings
Clustering query cells to reference centroids
Correcting query batch effects
UMAP
All done!
Warning: Adding features not currently present in the object

In leukemia samples, the distribution of mapping error scores can vary broadly from sample to sample. In this context, we will want to threshold outliers with high mapping error on a per-sample basis. Typically, a threshold of 2, 2.5, or 3 MADs works well.

In some cases where sequencing depth is very low (e.g. older datasets from first-generation scRNA-seq protocols), a more stringent threshold of even 1.5 may be warranted to eliminate cells with low mapping quality

# Run QC based on mapping error score, flag cells with mapping error >= 2.5 MADs above median
query <- query %>% calculate_MappingError(., reference = ref, MAD_threshold = 2.5, 
                                          threshold_by_donor = TRUE, donor_key = batchvar) # threshold mapping error on a per-sample basis.

# Plot distribution by patient to ensure you are catching the tail
query@meta.data %>% 
  ggplot(aes(x = mapping_error_score, fill = mapping_error_QC)) + 
  geom_histogram(bins = 200) + facet_wrap(.~get(batchvar))

# Get QC Plots
QC_plots <- plot_MappingErrorQC(query)

# Plot together - If this is too crowded, can also just call "QC_plots" aloneto display one by one
patchwork::wrap_plots(QC_plots, ncol = 4, widths = c(0.8, 0.3, 0.8, 0.3))

This important step identifies a subset of cells with high mapping error from the query dataset that are either:

Sometimes, low quality cells may erroneously map to the orthochromatic erythroblast region as this cell type has very low transcriptional diversity. These low quality query cells do not have hemoglobin expression and are in fact mis-mapped; they will be flagged by the QC filter and excluded from cell type assignments.

Please adjust the MAD_threshold (typically between 1 and 3) based on the distribution of your dataset to identify the outliers with low quality and high mapping error scores. This will improve your classifications and any downstream composition analysis

# # Optional step - remove outliers with high mapping error
# query <- subset(query, mapping_error_QC == 'Pass')

Optionally, outlier cells with high mapping error can also be removed at this stage. For ease of integrating these mapped annotations with the rest of your analysis, we can choose to skip this step. If so, Final CellType and Pseudotime predictions will be assigned as NA for cells failing the mapping error QC threshold.

Cell Type Assignments

We will next use a KNN classifier to assign cell identity based on the 30 K-Nearest Neighbours from the reference map. Broader cell type labels will also be transferred automatically along with the precise cell type labels. This label transfer step should take <1 min for 10,000 cells and <5 min for 100,000 cells.

# Predict Hematopoietic Cell Types by KNN classification
query <- predict_CellTypes(
  query_obj = query, 
  ref_obj = ref, 
  initial_label = 'initial_CellType', # celltype assignments before filtering on mapping QC
  final_label = 'predicted_CellType'  # celltype assignments with map QC failing cells assigned as NA
) 

DimPlot(subset(query, mapping_error_QC == 'Pass'), reduction = 'umap_projected', group.by = c('predicted_CellType'), 
        raster=FALSE, label=TRUE, label.size = 4)

We can also visualize the broader cell type categories in case precise labels are too granular. These provide a simpler view of the data and can help guide cluster annotations if your dataset does not have many cells.

DimPlot(subset(query, mapping_error_QC == 'Pass'), reduction = 'umap_projected', group.by = c('predicted_CellType_Broad'), 
        raster=FALSE, label=TRUE, label.size = 4)

Pseudotime Annotations

We can also annotate each query cell based on their position along hematopoietic pseudotime. Query cells will be assigned a pseudotime score based on the 30 K-Nearest Neighbours from the reference map. Since our Pseudotime KNN assignments are performed in UMAP space (more accurate than KNN on harmony components), this step is very fast (< 10s)

# Predict Pseudotime values by KNN
query <- predict_Pseudotime(
  query_obj = query, 
  ref_obj = ref, 
  initial_label = 'initial_Pseudotime',  # pseudotime assignments before filtering on mapping QC
  final_label = 'predicted_Pseudotime'   # pseudotime assignments with map QC failing cells assigned as NA
)

# Visualize Hematopoietic Pseudotime in query data
FeaturePlot(subset(query, mapping_error_QC == 'Pass'), features = c('predicted_Pseudotime'), split.by = 'Patient') & 
  scale_color_gradientn(colors = rev(brewer.pal(11, 'RdBu')))
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

Visualize Projection Density

Now let’s visualize the density distribution of query cells across the hematopoietic hierarchy

# Set batch/condition to be visualized individually
batch_key <- 'Patient'

# returns a list of plots for each donor from a pre-specified batch variable
projection_plots <- plot_Projection_byDonor(
  query_obj = query, 
  batch_key = batch_key, 
  ref_obj = ref, 
  Hierarchy_only = FALSE, # Whether to exclude T/NK/Plasma/Stromal cells 
  downsample_reference = TRUE, 
  downsample_frac = 0.25,   # down-sample reference cells to 25%; reduces figure file size
  query_point_size = 0.2,   # adjust size of query cells based on # of cells
  saveplot = TRUE, 
  save_folder = 'projectionFigures/'
)

# show plots together with patchwork. Can also just call "projection_plots" object to display one-by-one
patchwork::wrap_plots(projection_plots, ncol = 3)

We can also set Hierarchy_only = TRUE to remove T/NK/Plasma/Stromal cells and focus solely on the hematopoietic hierarchy.

# Set batch/condition to be visualized individually
batch_key <- 'Patient'

# returns a list of plots for each donor from a pre-specified batch variable
projection_plots <- plot_Projection_byDonor(
  query_obj = query, 
  batch_key = batch_key, 
  ref_obj = ref, 
  Hierarchy_only = TRUE, # Whether to exclude T/NK/Plasma/Stromal cells 
  downsample_reference = TRUE, 
  downsample_frac = 0.25,   # down-sample reference cells to 25%; reduces figure file size
  query_point_size = 0.2,   # adjust size of query cells based on # of cells
  saveplot = TRUE, 
  save_folder = 'projectionFigures/'
)

# show plots together with patchwork. Can also just call "projection_plots" object to display one-by-one
patchwork::wrap_plots(projection_plots, ncol = 3)

Get Composition data for each donor

Here, to study the abundance of each cell type within each donor, I focus on cells that were classified with a KNN prob > 0.5 (that is, >50% of nearest neighbours from the reference map agree on the assigned cell type).

We can present this as a long table

query_composition <- get_Composition(
  query_obj = query, 
  donor_key = 'Patient', 
  celltype_label = 'predicted_CellType', 
  mapQC_col = 'mapping_error_QC', 
  knn_prob_cutoff = 0.5, 
  return_type = 'long')

query_composition 

Or as a wide table with counts of # of cells

query_composition <- get_Composition(
  query_obj = query, 
  donor_key = 'Patient', 
  celltype_label = 'predicted_CellType', 
  mapQC_col = 'mapping_error_QC', 
  knn_prob_cutoff = 0.5, 
  return_type = 'count')

query_composition 

Or as a wide table with proportion of each cell type within each donor

query_composition <- get_Composition(
  query_obj = query, 
  donor_key = 'Patient', 
  celltype_label = 'predicted_CellType', 
  mapQC_col = 'mapping_error_QC', 
  knn_prob_cutoff = 0.5, 
  return_type = 'proportion')

query_composition 
# Simple heatmap to visualize composition of projected samples
p <- query_composition %>% 
  # show celltypes present in >1% of total cells
  select(Patient, colnames(query_composition)[-1][colSums(query_composition[-1]) > 0.01]) %>% 
  # convert to matrix and display heatmap
  column_to_rownames('Patient') %>% data.matrix() %>% ComplexHeatmap::Heatmap()
p

Extra: Score AML cells for enrichment of LSC signatures

Here we will load genesets derived from functional studies of LSC+ and LSC- fractions in diverse AML patients (Ng Nature 2017), and LSC+ vs LSC- fractions in patients with MLL translocations (Somervaille Cell Stem Cell 2009). The former tends to correspond to more primitive LSCs while the latter corresponds to a distinct signature of mature promonocytic LSCs.

Further, we will load additional genesets derived from marker genes of leukemia cell populations defined in van Galen et al Cell 2019 and Zeng et al Nature Medicine 2022. Notably, the LSPC-Quiescent population is highly associated with functional LSCs and relapse by deconvolution (Zeng 2022).

curl::curl_download('https://raw.githubusercontent.com/andygxzeng/BoneMarrowMap_Extras/main/scAML_Differentiation_Stage_MarkerGenes.gmt', 
                    destfile = paste0(projection_path, 'scAML_Differentiation_Stage_MarkerGenes.gmt'))
AMLgenesets <- load_Genesets_gmt('scAML_Differentiation_Stage_MarkerGenes.gmt')
Read 20 items
AMLgenesets %>% summary()
                                   Length Class  Mode     
HSC_MPP_like__Top100_scAML         100    -none- character
LMPP_like__Top100_scAML            100    -none- character
Early_Lymphoid_like__Top100_scAML  100    -none- character
ProB_PreB_like__Top100_scAML       100    -none- character
MEP_MkP_like__Top100_scAML         100    -none- character
Early_Erythroid_like__Top100_scAML 100    -none- character
Late_Erythroid_like__Top100_scAML  100    -none- character
EoBasoMast_like__Top100_scAML      100    -none- character
GMP_like__Top100_scAML             100    -none- character
ProMono_like__Top100_scAML         100    -none- character
Monocyte_like__Top100_scAML        100    -none- character
cDC_like__Top100_scAML             100    -none- character
pDC_like__Top100_scAML             100    -none- character
LSC_vs_Blast_UP__Ng2016             47    -none- character
LSC_vs_Blast_DOWN__Ng2016           56    -none- character
MLL_LSC_UP__Somervaille2009        176    -none- character
MLL_LSC_DOWN__Somervaille2009      353    -none- character
LSPC_Quiescent__Zeng2022            61    -none- character
LSPC_Primed__Top100_Zeng2022       100    -none- character
LSPC_Cycle__Top100_Zeng2022        100    -none- character
# score genesets by AUCell and add to metadata
# typically I split the data into batches of 5k-10k cells to conserve memory
query <- score_Genesets_AUCell(query, genesets = AMLgenesets, nbatches = 1, ncores = 10, output = 'metadata')
[1] "Running AUCell scoring"
[1] "batch 1"
Warning: sparse->dense coercion: allocating vector of size 2.0 GiBQuantiles for the number of genes detected by cell: 
(Non-detected genes are shuffled at the end of the ranking. Keep it in mind when choosing the threshold for calculating the AUC).
    min      1%      5%     10%     50%    100% 
 507.00  613.99  740.00  838.00 1596.00 7032.00 
Using 10 cores.
Using 10 cores with doMC.
Genes in the gene sets NOT available in the dataset: 
    ProB_PreB_like__Top100_scAML:   2 (2% of 100)
    EoBasoMast_like__Top100_scAML:  1 (1% of 100)
    LSC_vs_Blast_UP__Ng2016:    1 (2% of 47)
    LSC_vs_Blast_DOWN__Ng2016:  6 (11% of 56)
    MLL_LSC_UP__Somervaille2009:    9 (6% of 160)
    MLL_LSC_DOWN__Somervaille2009:  6 (2% of 284)
    LSPC_Quiescent__Zeng2022:   6 (10% of 61)
    LSPC_Primed__Top100_Zeng2022:   9 (9% of 100)
    LSPC_Cycle__Top100_Zeng2022:    2 (2% of 100)
Garbage collection 179 = 115+25+39 (level 2) ... 
559.0 Mbytes of cons cells used (66%)
2131.0 Mbytes of vectors used (22%)
[1] "Finished Scoring"
query@meta.data

Using genes associated with functional LSC+ engraftment capacity (LSC_vs_Blast_UP__Ng2016_AUC) along with the LSPC-Quiescent signature help us to identify candidate LSCs within the data.

FeaturePlot(subset(query, mapping_error_QC == 'Pass'), 
            features = c('LSPC_Quiescent__Zeng2022_AUC', 'LSC_vs_Blast_UP__Ng2016_AUC'), 
            split.by = 'Patient', max.cutoff = 'q99', min.cutoff = 'q5') & 
  scale_color_gradientn(colors = rev(brewer.pal(11, 'RdBu')))
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

It is no surprise that these are the most primitive cells earliest in Pseudotime. Note that this approach does not work for the MLL-AF9 samples wherein all leukemic cells are mature myeloid; in this context the MLL_LSC_Somervaille_2009_UP signature may help us identify functional “LSCs” capable of engraftment within the mature myeloid populations (typically late GMPs or Early ProMonocytes).

FeaturePlot(subset(query, mapping_error_QC == 'Pass'), 
            features = c('MLL_LSC_UP__Somervaille2009_AUC'), 
            split.by = 'Patient', max.cutoff = 'q99', min.cutoff = 'q5') & 
  scale_color_gradientn(colors = rev(brewer.pal(11, 'RdBu')))
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

As expected, ProMonocytes within the MLL-AF9 sample exhibit enrichment for the MLL_LSC_Somervaille_2009_UP, in line with the mature myeloid origin of this patient’s disease.

Save projection results

This will save a csv file with the mapped annotations for each cell (mapping error scores, umap coordinates, predicted Cell Type, and predicted Pseudotime). We also have the option to save AUCell scores, provided that they are in the metadata.

# Save CellType Annotations and Projected UMAP coordinates
save_ProjectionResults(
  query_obj = query, 
  celltype_label = 'predicted_CellType', 
  celltype_KNNprob_label = 'predicted_CellType_prob', 
  pseudotime_label = 'predicted_Pseudotime', 
  save_AUCell_scores = TRUE,
  file_name = 'querydata_projected_labeled.csv')

Note on Downstream Analysis:

For downstream analysis, you can use the projected celltype labels to help annotate any leukemia cell clusters generated through unsupervised dimensionality reduction and clustering from individual patients. Sometimes, unsupervised analysis will yield clusters corresponding to different developmental states and other times it may yield clusters corresponding to distinct subclones within the patient. Along with tools like inferCNV for patients with known cytogenetic abnormalities, this can be integrated to visualize cellular hierarchies at the level of individual subclones.

Additional information from pseudotime projection and scoring of LSC-specific signatures can help identify candidate LSCs within the data.

For downstream analysis, let’s take the example of pt30886, with involvement of HSCs and multiple trajectories spanning Myeloid, Erythroid, and pDC differentiation:

pt30886 <- query %>%
  subset(., Patient == 'pt_30886') %>% 
  SCTransform() %>% 
  RunPCA() %>% 
  RunUMAP(reduction = 'pca', dims=1:20) %>%
  FindNeighbors(reduction = 'pca', dims = 1:20) %>% 
  FindClusters(resolution = 1, algorithm = 3)
Calculating cell attributes from input UMI matrix: log_umi
Variance stabilizing transformation of count matrix of size 15690 by 2500
Model formula is y ~ log_umi
Get Negative Binomial regression parameters per gene
Using 2000 genes, 2500 cells

  |                                                                                                                                                                                                                          
  |                                                                                                                                                                                                                    |   0%
  |                                                                                                                                                                                                                          
  |=====================================================                                                                                                                                                               |  25%
  |                                                                                                                                                                                                                          
  |==========================================================================================================                                                                                                          |  50%
  |                                                                                                                                                                                                                          
  |===============================================================================================================================================================                                                     |  75%
  |                                                                                                                                                                                                                          
  |====================================================================================================================================================================================================================| 100%
Found 84 outliers - those will be ignored in fitting/regularization step

Second step: Get residuals using fitted parameters for 15690 genes

  |                                                                                                                                                                                                                          
  |                                                                                                                                                                                                                    |   0%
  |                                                                                                                                                                                                                          
  |=======                                                                                                                                                                                                             |   3%
  |                                                                                                                                                                                                                          
  |=============                                                                                                                                                                                                       |   6%
  |                                                                                                                                                                                                                          
  |====================                                                                                                                                                                                                |   9%
  |                                                                                                                                                                                                                          
  |==========================                                                                                                                                                                                          |  12%
  |                                                                                                                                                                                                                          
  |=================================                                                                                                                                                                                   |  16%
  |                                                                                                                                                                                                                          
  |========================================                                                                                                                                                                            |  19%
  |                                                                                                                                                                                                                          
  |==============================================                                                                                                                                                                      |  22%
  |                                                                                                                                                                                                                          
  |=====================================================                                                                                                                                                               |  25%
  |                                                                                                                                                                                                                          
  |============================================================                                                                                                                                                        |  28%
  |                                                                                                                                                                                                                          
  |==================================================================                                                                                                                                                  |  31%
  |                                                                                                                                                                                                                          
  |=========================================================================                                                                                                                                           |  34%
  |                                                                                                                                                                                                                          
  |================================================================================                                                                                                                                    |  38%
  |                                                                                                                                                                                                                          
  |======================================================================================                                                                                                                              |  41%
  |                                                                                                                                                                                                                          
  |=============================================================================================                                                                                                                       |  44%
  |                                                                                                                                                                                                                          
  |===================================================================================================                                                                                                                 |  47%
  |                                                                                                                                                                                                                          
  |==========================================================================================================                                                                                                          |  50%
  |                                                                                                                                                                                                                          
  |=================================================================================================================                                                                                                   |  53%
  |                                                                                                                                                                                                                          
  |=======================================================================================================================                                                                                             |  56%
  |                                                                                                                                                                                                                          
  |==============================================================================================================================                                                                                      |  59%
  |                                                                                                                                                                                                                          
  |====================================================================================================================================                                                                                |  62%
  |                                                                                                                                                                                                                          
  |===========================================================================================================================================                                                                         |  66%
  |                                                                                                                                                                                                                          
  |==================================================================================================================================================                                                                  |  69%
  |                                                                                                                                                                                                                          
  |========================================================================================================================================================                                                            |  72%
  |                                                                                                                                                                                                                          
  |===============================================================================================================================================================                                                     |  75%
  |                                                                                                                                                                                                                          
  |======================================================================================================================================================================                                              |  78%
  |                                                                                                                                                                                                                          
  |============================================================================================================================================================================                                        |  81%
  |                                                                                                                                                                                                                          
  |===================================================================================================================================================================================                                 |  84%
  |                                                                                                                                                                                                                          
  |==========================================================================================================================================================================================                          |  88%
  |                                                                                                                                                                                                                          
  |================================================================================================================================================================================================                    |  91%
  |                                                                                                                                                                                                                          
  |=======================================================================================================================================================================================================             |  94%
  |                                                                                                                                                                                                                          
  |=============================================================================================================================================================================================================       |  97%
  |                                                                                                                                                                                                                          
  |====================================================================================================================================================================================================================| 100%
Computing corrected count matrix for 15690 genes

  |                                                                                                                                                                                                                          
  |                                                                                                                                                                                                                    |   0%
  |                                                                                                                                                                                                                          
  |=======                                                                                                                                                                                                             |   3%
  |                                                                                                                                                                                                                          
  |=============                                                                                                                                                                                                       |   6%
  |                                                                                                                                                                                                                          
  |====================                                                                                                                                                                                                |   9%
  |                                                                                                                                                                                                                          
  |==========================                                                                                                                                                                                          |  12%
  |                                                                                                                                                                                                                          
  |=================================                                                                                                                                                                                   |  16%
  |                                                                                                                                                                                                                          
  |========================================                                                                                                                                                                            |  19%
  |                                                                                                                                                                                                                          
  |==============================================                                                                                                                                                                      |  22%
  |                                                                                                                                                                                                                          
  |=====================================================                                                                                                                                                               |  25%
  |                                                                                                                                                                                                                          
  |============================================================                                                                                                                                                        |  28%
  |                                                                                                                                                                                                                          
  |==================================================================                                                                                                                                                  |  31%
  |                                                                                                                                                                                                                          
  |=========================================================================                                                                                                                                           |  34%
  |                                                                                                                                                                                                                          
  |================================================================================                                                                                                                                    |  38%
  |                                                                                                                                                                                                                          
  |======================================================================================                                                                                                                              |  41%
  |                                                                                                                                                                                                                          
  |=============================================================================================                                                                                                                       |  44%
  |                                                                                                                                                                                                                          
  |===================================================================================================                                                                                                                 |  47%
  |                                                                                                                                                                                                                          
  |==========================================================================================================                                                                                                          |  50%
  |                                                                                                                                                                                                                          
  |=================================================================================================================                                                                                                   |  53%
  |                                                                                                                                                                                                                          
  |=======================================================================================================================                                                                                             |  56%
  |                                                                                                                                                                                                                          
  |==============================================================================================================================                                                                                      |  59%
  |                                                                                                                                                                                                                          
  |====================================================================================================================================                                                                                |  62%
  |                                                                                                                                                                                                                          
  |===========================================================================================================================================                                                                         |  66%
  |                                                                                                                                                                                                                          
  |==================================================================================================================================================                                                                  |  69%
  |                                                                                                                                                                                                                          
  |========================================================================================================================================================                                                            |  72%
  |                                                                                                                                                                                                                          
  |===============================================================================================================================================================                                                     |  75%
  |                                                                                                                                                                                                                          
  |======================================================================================================================================================================                                              |  78%
  |                                                                                                                                                                                                                          
  |============================================================================================================================================================================                                        |  81%
  |                                                                                                                                                                                                                          
  |===================================================================================================================================================================================                                 |  84%
  |                                                                                                                                                                                                                          
  |==========================================================================================================================================================================================                          |  88%
  |                                                                                                                                                                                                                          
  |================================================================================================================================================================================================                    |  91%
  |                                                                                                                                                                                                                          
  |=======================================================================================================================================================================================================             |  94%
  |                                                                                                                                                                                                                          
  |=============================================================================================================================================================================================================       |  97%
  |                                                                                                                                                                                                                          
  |====================================================================================================================================================================================================================| 100%
Calculating gene attributes
Wall clock passed: Time difference of 26.35595 secs
Determine variable features
Place corrected count matrix in counts slot
Centering data matrix

  |                                                                                                                                                                                                                          
  |                                                                                                                                                                                                                    |   0%
  |                                                                                                                                                                                                                          
  |=====================================================                                                                                                                                                               |  25%
  |                                                                                                                                                                                                                          
  |==========================================================================================================                                                                                                          |  50%
  |                                                                                                                                                                                                                          
  |===============================================================================================================================================================                                                     |  75%
  |                                                                                                                                                                                                                          
  |====================================================================================================================================================================================================================| 100%
Set default assay to SCT
PC_ 1 
Positive:  HBB, AHSP, HBD, PRDX2, HBA1, CA1, HBA2, HBM, BLVRB, ALAS2 
       SLC25A37, HMBS, SLC4A1, IFI27, CA2, SLC25A39, HIST1H4C, MYL4, UROD, SLC2A1 
       HBG2, EPB42, RHAG, SMIM1, S100A6, DMTN, SELENBP1, UBAC1, HIST1H1E, BSG 
Negative:  CD74, MALAT1, HLA-B, HLA-DRA, HLA-DRB1, PMAIP1, BTG1, IFIT2, MX1, HLA-DPB1 
       HLA-A, AREG, SRGN, CXCR4, HLA-DPA1, CYBA, JCHAIN, FOS, SOX4, SAT1 
       EEF1A1, UBC, RPS27, OASL, HLA-C, RPL10, ZFP36L2, TMSB4X, HLA-DQB1, GPR183 
PC_ 2 
Positive:  GAPDH, SNHG29, RPLP0, C1QTNF4, PRSS57, GIHCG, PRDX1, RPS19, SERPINB1, AKR1A1 
       RPL13, MDK, GAS5, IFITM3, GSTP1, APOE, NAA38, ACTG1, VIM, TPI1 
       APOC1, CD34, BEX1, STMN1, NPDC1, SPINK2, CST3, FCER1A, SMIM24, RPL10 
Negative:  MALAT1, BTG1, CCL5, ISG20, MT-CO1, NKG7, HBM, HBA2, HBA1, CXCR4 
       HLA-B, SLC4A1, CA1, AHSP, MT-CO3, SLC25A37, CD7, TNFAIP3, CD3E, CD69 
       PRDX2, ADGRE5, CST7, XCL1, XCL2, NR4A2, UBC, RNF19A, SRGN, HBB 
PC_ 3 
Positive:  IFIT2, PMAIP1, CCL5, IFIT3, MT2A, OASL, CD7, TNFAIP3, MT-CO3, CCL3L1 
       CST7, ISG15, CD69, ISG20, CD247, IFIH1, CD3E, IFITM1, NKG7, MT-CO1 
       IFNB1, ZC3HAV1, XCL2, GZMM, XCL1, MT-CYB, IFIT1, HERC5, SPOCK2, TNF 
Negative:  CD74, JCHAIN, HLA-DRB1, HLA-DRA, HLA-DPB1, MX1, IGKC, PLD4, HLA-DPA1, LGMN 
       HLA-DQB1, SOX4, PLP2, AKAP13, CYBA, HERPUD1, CORO1A, AREG, LIME1, GPR183 
       IRF8, HLA-DRB5, HLA-DMA, SCT, SPIB, HLA-DQA1, PLAUR, SLC15A4, ITM2C, GPR65 
PC_ 4 
Positive:  HBB, S100A6, LYZ, ACTB, COTL1, CST3, S100A4, S100A10, HIST1H1E, TMSB10 
       PFN1, HBG2, IGKV3-15, IFI30, TMSB4X, TIMP1, FLNA, SH3BGRL3, EMP3, IGKV3-20 
       FTH1, VIM, ITGA2B, LGALS1, IGHV3-74, S100A9, TPI1, ANXA1, APOE, LMNA 
Negative:  C1QTNF4, IFIT2, PMAIP1, SPINK2, MDK, GYPC, SLC25A37, IFIT3, SLC4A1, IFNB1 
       GAS5, SMIM24, NPDC1, IGHM, CXCL10, HBA1, IFIH1, HBA2, SLC2A1, IFI27 
       HERC5, ZFP36L2, BEX1, SLC25A39, CRHBP, HEMGN, HMBS, ALAS2, EPB42, HBM 
PC_ 5 
Positive:  HBB, IFIT2, CCL3L1, CCL3, IFNB1, PMAIP1, IFIT3, CXCL10, HBG2, VIM 
       HIST1H1E, IGKV3-20, IGKV3-15, C1QTNF4, LYZ, IFIH1, CXCL11, IGHV3-74, CXCL8, HBM 
       NFKBIZ, S100A9, IFNL1, MPO, JUN, PTGS2, ATF3, BCL2A1, S100A8, CSF3R 
Negative:  APOC1, APOE, MYL4, EEF1A1, CD7, RPS27, CD3E, PVT1, FCER1A, IFITM1 
       NAA38, CNRIP1, BTG1, HLA-B, ATF7IP2, HES6, HERPUD1, ODC1, RNASE1, ATP5IF1 
       FAM178B, BLVRB, SDF2L1, CD247, RPS27L, TMSB4X, ITGA2B, MT-CO3, UROD, JCHAIN 
19:24:57 UMAP embedding parameters a = 0.9922 b = 1.112
19:24:57 Read 2500 rows and found 20 numeric columns
19:24:57 Using Annoy for neighbor search, n_neighbors = 30
19:24:57 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
19:24:57 Writing NN index file to temp file /var/folders/2b/f9nktt6d0wzfcpxv5xxm2n900000gp/T//RtmpGCyUkO/file11ec27047390
19:24:57 Searching Annoy index using 1 thread, search_k = 3000
19:24:57 Annoy recall = 100%
19:24:58 Commencing smooth kNN distance calibration using 1 thread with target n_neighbors = 30
19:25:00 Initializing from normalized Laplacian + noise (using irlba)
19:25:00 Commencing optimization for 500 epochs, with 103452 positive edges
Using method 'umap'
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
19:25:03 Optimization finished
Computing nearest neighbor graph
Computing SNN
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 2500
Number of edges: 91096

Running smart local moving algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.8258
Number of communities: 16
Elapsed time: 0 seconds
DimPlot(pt30886, reduction = 'umap', group.by=c('seurat_clusters', 'predicted_CellType_Broad'), label = T)

FeaturePlot(pt30886, 
            features = c('LSPC_Quiescent__Zeng2022_AUC', 'LSC_vs_Blast_UP__Ng2016_AUC', 'HSC_MPP_like__Top100_scAML_AUC'), 
            max.cutoff = 'q99', min.cutoff = 'q5', ncol = 3) & 
  scale_color_gradientn(colors = rev(brewer.pal(11, 'RdBu')))
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.

Based on this we can see multiple transcriptional clusters within the data, including some non-leukemic clusters of T cells, B cells, and Plasma cells. Cluster 3 contains HSC/MPP-like cells most enriched for LSC signatures.

Let’s see what each cluster looks like when projected onto BoneMarrowMap:

DimPlot(pt30886, reduction = 'umap_projected', group.by=c('seurat_clusters', 'predicted_CellType_Broad'), label = T)

pt30886@meta.data %>% 
  # compare clusters against broad cell types assignments 
  group_by(predicted_CellType_Broad, seurat_clusters) %>% 
  summarise(count = n()) %>% ungroup() %>% 
  # get proportions of cells within each cluster assigned as a certain celltype 
  group_by(seurat_clusters) %>% mutate(prop = count / sum(count)) %>% 
  pivot_wider(id_cols = 'predicted_CellType_Broad', names_from = 'seurat_clusters', values_from = 'prop') %>% 
  # fill in zeros and format for heatmap 
  replace(is.na(.), 0) %>% column_to_rownames('predicted_CellType_Broad') %>% 
  # create a basic heatmap to visualize cell types vs clusters
  pheatmap::pheatmap()
`summarise()` has grouped output by 'predicted_CellType_Broad'. You can override using the `.groups` argument.

This illustrates how annotations from BoneMarrowMap can allow us to analyze clusters of leukemic cells identified from unsupervised analysis. As shown in the heatmap above, each cluster from this patient actually corresponds to a distinct state along the hematopoietic hierarchy, with multiple clusters corresponding to erythroid progenitors. Overlaying copy number alteration calls from inferCNV or expressed mutations can help us identify the differentiation patterns of distinct subclones within this patient.

Finally, for cohorts with many patients composition analysis can be performed to understand how leukemia cell hierarchies vary with patient characteristics and therapy response / relapse. I hope this tutorial was useful and feel free to comment with any questions you may have around projection of leukemia cells or downstream analysis.

LS0tCnRpdGxlOiAiTGV1a2VtaWEgUHJvamVjdGlvbiBUdXRvcmlhbCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKSGVyZSB3ZSB3aWxsIG1hcCBhbiBleGFtcGxlIGRhdGFzZXQgZnJvbSB0aHJlZSBsZXVrZW1pYXMgc2VxdWVuY2VkIGluIG91ciBzdHVkeS4KVGhpcyBlbnRpcmUgdHV0b3JpYWwgc2hvdWxkIHRha2UgbGVzcyB0aGFuIDEwIG1pbnV0ZXMgdG8gcnVuIAoKIyMjIFNldHVwCiAKYGBge3J9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzeW1waG9ueSkKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKYGBgCgpJbnN0YWxsIHBhY2thZ2UgZnJvbSBnaXRodWIKCmBgYHtyLCBldmFsPUZBTFNFfQojIyBpbnN0YWxsIGRlcGVuZGVuY2llcyB0aGF0IGFyZSBub3Qgb24gQ1JBTgppZighcmVxdWlyZShCaW9jTWFuYWdlciwgcXVpZXRseSA9IFRSVUUpKSBpbnN0YWxsLnBhY2thZ2VzKCJCaW9jTWFuYWdlciIpCkJpb2NNYW5hZ2VyOjppbnN0YWxsKGMoIkFVQ2VsbCIsICJkb01DIiwgIkJpb2NOZWlnaGJvcnMiKSkKaWYoIXJlcXVpcmUoZGV2dG9vbHMsIHF1aWV0bHkgPSBUUlVFKSkgaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImphcmVkaHVsaW5nL2pjb2xvcnMiKQpgYGAKIApgYGB7ciwgZXZhbD1GQUxTRX0KIyMgaW5zdGFsbCBCb25lTWFycm93TWFwIHBhY2thZ2UKZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCdhbmR5Z3h6ZW5nL0JvbmVNYXJyb3dNYXAnLCBmb3JjZSA9IFRSVUUpCmBgYAoKYGBge3J9CmxpYnJhcnkoQm9uZU1hcnJvd01hcCkKYGBgCgojIyMjIERvd25sb2FkIHJlZmVyZW5jZSBvYmplY3QgYW5kIFVNQVAgbW9kZWwKClNldCBwcm9qZWN0aW9uIGZvbGRlciwgRG93bmxvYWQgcmVmZXJlbmNlIG9iamVjdCBhbmQgVU1BUCBtb2RlbAoKYGBge3IsIGV2YWw9RkFMU0V9CiMgU2V0IGRpcmVjdG9yeSB0byBzdG9yZSBwcm9qZWN0aW9uIHJlZmVyZW5jZSBmaWxlcwpwcm9qZWN0aW9uX3BhdGggPSAnLi8nCgojIERvd25sb2FkIEJvbmUgTWFycm93IFJlZmVyZW5jZSAtIDM0NCBNYgpjdXJsOjpjdXJsX2Rvd25sb2FkKCdodHRwczovL2JvbmVtYXJyb3dtYXAuczMudXMtZWFzdC0yLmFtYXpvbmF3cy5jb20vQm9uZU1hcnJvd01hcF9TeW1waG9ueVJlZmVyZW5jZS5yZHMnLCAKICAgICAgICAgICAgICAgICAgICBkZXN0ZmlsZSA9IHBhc3RlMChwcm9qZWN0aW9uX3BhdGgsICdCb25lTWFycm93TWFwX1N5bXBob255UmVmZXJlbmNlLnJkcycpKQojIERvd25sb2FkIHV3b3QgbW9kZWwgZmlsZSAtIDIyMSBNYgpjdXJsOjpjdXJsX2Rvd25sb2FkKCdodHRwczovL2JvbmVtYXJyb3dtYXAuczMudXMtZWFzdC0yLmFtYXpvbmF3cy5jb20vQm9uZU1hcnJvd01hcF91d290X21vZGVsLnV3b3QnLCAKICAgICAgICAgICAgICAgICAgICBkZXN0ZmlsZSA9IHBhc3RlMChwcm9qZWN0aW9uX3BhdGgsICdCb25lTWFycm93TWFwX3V3b3RfbW9kZWwudXdvdCcpKQpgYGAKCgpgYGB7cn0KcHJvamVjdGlvbl9wYXRoID0gJy4vJwojIExvYWQgU3ltcGhvbnkgcmVmZXJlbmNlCnJlZiA8LSByZWFkUkRTKHBhc3RlMChwcm9qZWN0aW9uX3BhdGgsICdCb25lTWFycm93TWFwX1N5bXBob255UmVmZXJlbmNlLnJkcycpKQojIFNldCB1d290IHBhdGggZm9yIFVNQVAgcHJvamVjdGlvbgpyZWYkc2F2ZV91d290X3BhdGggPC0gcGFzdGUwKHByb2plY3Rpb25fcGF0aCwgJ0JvbmVNYXJyb3dNYXBfdXdvdF9tb2RlbC51d290JykKYGBgCgoKIyMjIyBWaXN1YWxpemUgQm9uZSBNYXJyb3cgUmVmZXJlbmNlCgpJZiB3ZSB3YW50IHRvIHZpc3VhbGl6ZSBjZWxsdHlwZSBsYWJlbHMgb3IgbWV0YWRhdGEgZnJvbSB0aGUgQk0gUmVmZXJlbmNlLCB3ZSBjYW4gY3JlYXRlIGEgU2V1cmF0IE9iamVjdCBmcm9tIHRoZSBzeW1waG9ueSByZWZlcmVuY2UgClRoaXMgd2lsbCBiZSBtZW1vcnkgZWZmaWNpZW50IGFzIGl0IHdpbGwgbm90IGluY2x1ZGUgZ2VuZSBleHByZXNzaW9uIGNvdW50cywgb25seSB0aGUgVU1BUCBjb29yZGluYXRlcyBhbmQgdGhlIG1ldGFkYXRhIGluY2x1ZGluZyBjZWxsIGxhYmVscyBhbmQgc29ydGluZyBpbmZvcm1hdGlvbgoKYGBge3IsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTExfQpSZWZlcmVuY2VTZXVyYXRPYmogPC0gY3JlYXRlX1JlZmVyZW5jZU9iamVjdChyZWYpCgpEaW1QbG90KFJlZmVyZW5jZVNldXJhdE9iaiwgcmVkdWN0aW9uID0gJ3VtYXAnLCBncm91cC5ieSA9ICdDZWxsVHlwZV9Bbm5vdGF0aW9uX2Zvcm1hdHRlZCcsIAogICAgICAgIHJhc3Rlcj1GQUxTRSwgbGFiZWw9VFJVRSwgbGFiZWwuc2l6ZSA9IDQpCmBgYApXZSBjYW4gYWxzbyB2aXN1YWxpemUgYnJvYWRlciBjZWxsIHR5cGUgbGFiZWxzIHdoaWNoIG1heSBzaW1wbGlmeSBpbnRlcnByZXRhdGlvbiBhbmQgZG93bnN0cmVhbSBhbmFseXNpcy4gCgpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTF9CkRpbVBsb3QoUmVmZXJlbmNlU2V1cmF0T2JqLCByZWR1Y3Rpb24gPSAndW1hcCcsIGdyb3VwLmJ5ID0gJ0NlbGxUeXBlX0Jyb2FkJywgCiAgICAgICAgcmFzdGVyPUZBTFNFLCBsYWJlbD1UUlVFLCBsYWJlbC5zaXplID0gNCkKYGBgCgpXZSBjYW4gdmlzdWFsaXplIG90aGVyIGFubm90YXRpb25zIHRvbywgaW5jbHVkaW5nIGNlbGwgY3ljbGUgcGhhc2UgYW5kIGxpbmVhZ2UgcHNldWRvdGltZSBlc3RpbWF0ZXMuCgpgYGB7ciwgZmlnLmhlaWdodD0zLjUsIGZpZy53aWR0aD0xMX0KcDEgPC0gRGltUGxvdChSZWZlcmVuY2VTZXVyYXRPYmosIHJlZHVjdGlvbiA9ICd1bWFwJywgZ3JvdXAuYnkgPSAnQ3ljbGVQaGFzZScsIHJhc3Rlcj1GQUxTRSkKcDIgPC0gRmVhdHVyZVBsb3QoUmVmZXJlbmNlU2V1cmF0T2JqLCByZWR1Y3Rpb24gPSAndW1hcCcsIGZlYXR1cmVzID0gJ1BzZXVkb3RpbWUnLCByYXN0ZXI9RkFMU0UpIAoKcDEgKyBwMgpgYGAKCgojIyMjIExvYWQgTGV1a2VtaWEgc2NSTkEtc2VxIGRhdGEuCgpXZSBoYXZlIGNvbmZpZGVudGx5IG1hcHBlZCBsZXVrZW1pYXMgc3Bhbm5pbmcgQU1MIChpbmNsLiBBTUtMIGFuZCBBRUwpLCBCLUFMTCwgTVBBTCwgTURTLCBDTUwsIE1QTiwgYW5kIEJEUENOLCBhY3Jvc3Mgc2VxdWVuY2luZyB0ZWNobm9sb2dpZXMuCkhvd2V2ZXIsIHdlIGNhbm5vdCBiZSBjb25maWRlbnQgaW4gbWFwcGluZyBULUFMTCBkdWUgdG8gYSBsYWNrIG9mIHRoeW11cy1zcGVjaWZpYyByZWZlcmVuY2UgZGF0YSBvbiBUIGNlbGwgcHJlY3Vyc29yIHN0YWdlcy4gRnVydGhlciwgb3VyIHRvb2wgZG9lcyBub3QgZGlzY3JpbWluYXRlIGJldHdlZW4gbm9ybWFsIHZzIG1hbGlnbmFudCBjZWxscywgd2hpY2ggaXMgYW4gaW1wb3J0YW50IGNvbnNpZGVyYXRpb24gcGFydGljdWxhcmx5IHdpdGhpbiBsb3ctYmxhc3QgY291bnQgY2hyb25pYyBkaXNlYXNlcy4gCgoKQXMgYW4gZXhhbXBsZSBoZXJlLCB3ZSBhcmUgZ29pbmcgdG8gcHJvamVjdCBzY1JOQS1zZXEgZGF0YSBmcm9tIHRocmVlIGRpdmVyc2UgQU1MIHBhdGllbnRzIHNlcXVlbmNlZCBpbiBvdXIgc3R1ZHkuIAoKKiBwdF8xNzg0NDogTUxMLUFGOSB0cmFuc2xvY2F0aW9uIHdpdGggYSBwdXJlbHkgbWF0dXJlIGxldWtlbWlhIGNlbGwgaGllcmFyY2h5CiogcHRfMTc3NDY6IE5QTTFjICsgRE5NVDNBICsgVEVUMiB3aXRoIGEgR01QLWRvbWluYW50IGxldWtlbWlhIGNlbGwgaGllcmFyY2h5CiogcHRfMzA4ODY6IENvbXBsZXggS2FyeW90eXBlIHdpdGggYSBwcmltaXRpdmUgbGV1a2VtaWEgY2VsbCBoaWVyYXJjaHkgKyBleHRlbnNpdmUgZXJ5dGhyb2lkIGludm9sdmVtZW50IAoKRm9yIGNvbnZlbmllbmNlLCBlYWNoIHBhdGllbnQgc2FtcGxlIHdhcyBkb3duc2FtcGxlZCB0byAyNTAwIGNlbGxzLiAKCmBgYHtyLCBldmFsPUZBTFNFfQpjdXJsOjpjdXJsX2Rvd25sb2FkKCdodHRwczovL2JvbmVtYXJyb3dtYXAuczMudXMtZWFzdC0yLmFtYXpvbmF3cy5jb20vRXhhbXBsZVF1ZXJ5X0FNTF9zY1JOQXNlcS5yZHMnLCAKICAgICAgICAgICAgICAgICAgICBkZXN0ZmlsZSA9IHBhc3RlMChwcm9qZWN0aW9uX3BhdGgsICdFeGFtcGxlUXVlcnlfQU1MX3NjUk5Bc2VxLnJkcycpKQpgYGAKCgpgYGB7cn0KcXVlcnkgPC0gcmVhZFJEUygnRXhhbXBsZVF1ZXJ5X0FNTF9zY1JOQXNlcS5yZHMnKQpxdWVyeQpgYGAKCgojIyMgTWFwIHRoZSBRdWVyeSBEYXRhClByb3ZpZGUgcmF3IGNvdW50cywgbWV0YWRhdGEsIGFuZCBkb25vciBrZXkuIFRoaXMgc2hvdWxkIHRha2UgPDEgbWluCkNhbGN1bGF0ZSBtYXBwaW5nIGVycm9yIGFuZCBwZXJmb3JtIFFDIHRvIHJlbW92ZSBsb3cgcXVhbGl0eSBjZWxscyB3aXRoIGhpZ2ggbWFwcGluZyBlcnJvcgoKYGBge3J9CiMgYmF0Y2ggdmFyaWFibGUgdG8gY29ycmVjdCBpbiB0aGUgcXVlcnkgZGF0YSwgc2V0IGFzIE5VTEwgaWYgbm8gYmF0Y2hlcyBpbiBxdWVyeQpiYXRjaHZhciA8LSAnUGF0aWVudCcKCiMgTWFwIHF1ZXJ5IGRhdGFzZXQgdXNpbmcgU3ltcGhvbnkgKEthbmcgZXQgYWwgMjAyMSkKcXVlcnkgPC0gbWFwX1F1ZXJ5KAogICAgZXhwX3F1ZXJ5ID0gcXVlcnlbWydSTkEnXV1AY291bnRzLCAKICAgIG1ldGFkYXRhX3F1ZXJ5ID0gcXVlcnlAbWV0YS5kYXRhLAogICAgcmVmX29iaiA9IHJlZiwKICAgIHZhcnMgPSBiYXRjaHZhcgopCmBgYAoKSW4gbGV1a2VtaWEgc2FtcGxlcywgdGhlIGRpc3RyaWJ1dGlvbiBvZiBtYXBwaW5nIGVycm9yIHNjb3JlcyBjYW4gdmFyeSBicm9hZGx5IGZyb20gc2FtcGxlIHRvIHNhbXBsZS4gSW4gdGhpcyBjb250ZXh0LCB3ZSB3aWxsIHdhbnQgdG8gdGhyZXNob2xkIG91dGxpZXJzIHdpdGggaGlnaCBtYXBwaW5nIGVycm9yIG9uIGEgcGVyLXNhbXBsZSBiYXNpcy4gVHlwaWNhbGx5LCBhIHRocmVzaG9sZCBvZiAyLCAyLjUsIG9yIDMgTUFEcyB3b3JrcyB3ZWxsLiAKCkluIHNvbWUgY2FzZXMgd2hlcmUgc2VxdWVuY2luZyBkZXB0aCBpcyB2ZXJ5IGxvdyAoZS5nLiBvbGRlciBkYXRhc2V0cyBmcm9tIGZpcnN0LWdlbmVyYXRpb24gc2NSTkEtc2VxIHByb3RvY29scyksIGEgbW9yZSBzdHJpbmdlbnQgdGhyZXNob2xkIG9mIGV2ZW4gMS41IG1heSBiZSB3YXJyYW50ZWQgdG8gZWxpbWluYXRlIGNlbGxzIHdpdGggbG93IG1hcHBpbmcgcXVhbGl0eSAKCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0xMH0KIyBSdW4gUUMgYmFzZWQgb24gbWFwcGluZyBlcnJvciBzY29yZSwgZmxhZyBjZWxscyB3aXRoIG1hcHBpbmcgZXJyb3IgPj0gMi41IE1BRHMgYWJvdmUgbWVkaWFuCnF1ZXJ5IDwtIHF1ZXJ5ICU+JSBjYWxjdWxhdGVfTWFwcGluZ0Vycm9yKC4sIHJlZmVyZW5jZSA9IHJlZiwgTUFEX3RocmVzaG9sZCA9IDIuNSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRocmVzaG9sZF9ieV9kb25vciA9IFRSVUUsIGRvbm9yX2tleSA9IGJhdGNodmFyKSAjIHRocmVzaG9sZCBtYXBwaW5nIGVycm9yIG9uIGEgcGVyLXNhbXBsZSBiYXNpcy4KCiMgUGxvdCBkaXN0cmlidXRpb24gYnkgcGF0aWVudCB0byBlbnN1cmUgeW91IGFyZSBjYXRjaGluZyB0aGUgdGFpbApxdWVyeUBtZXRhLmRhdGEgJT4lIAogIGdncGxvdChhZXMoeCA9IG1hcHBpbmdfZXJyb3Jfc2NvcmUsIGZpbGwgPSBtYXBwaW5nX2Vycm9yX1FDKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjAwKSArIGZhY2V0X3dyYXAoLn5nZXQoYmF0Y2h2YXIpKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0xMH0KIyBHZXQgUUMgUGxvdHMKUUNfcGxvdHMgPC0gcGxvdF9NYXBwaW5nRXJyb3JRQyhxdWVyeSkKCiMgUGxvdCB0b2dldGhlciAtIElmIHRoaXMgaXMgdG9vIGNyb3dkZWQsIGNhbiBhbHNvIGp1c3QgY2FsbCAiUUNfcGxvdHMiIGFsb25ldG8gZGlzcGxheSBvbmUgYnkgb25lCnBhdGNod29yazo6d3JhcF9wbG90cyhRQ19wbG90cywgbmNvbCA9IDQsIHdpZHRocyA9IGMoMC44LCAwLjMsIDAuOCwgMC4zKSkKYGBgCgoKVGhpcyBpbXBvcnRhbnQgc3RlcCBpZGVudGlmaWVzIGEgc3Vic2V0IG9mIGNlbGxzIHdpdGggaGlnaCBtYXBwaW5nIGVycm9yIGZyb20gdGhlIHF1ZXJ5IGRhdGFzZXQgdGhhdCBhcmUgZWl0aGVyOgoKKiBub3QgcHJlc2VudCB3aXRoaW4gdGhlIHJlZmVyZW5jZSwgb3IKKiBoYXZlIHBvb3IgUUMgbWV0cmljcyAobG93IFJOQSBjb3VudHMgYW5kIGxvdyB0cmFuc2NyaXB0aW9uYWwgZGl2ZXJzaXR5KQoKU29tZXRpbWVzLCBsb3cgcXVhbGl0eSBjZWxscyBtYXkgZXJyb25lb3VzbHkgbWFwIHRvIHRoZSBvcnRob2Nocm9tYXRpYyBlcnl0aHJvYmxhc3QgcmVnaW9uIGFzIHRoaXMgY2VsbCB0eXBlIGhhcyB2ZXJ5IGxvdyB0cmFuc2NyaXB0aW9uYWwgZGl2ZXJzaXR5LiAKVGhlc2UgbG93IHF1YWxpdHkgcXVlcnkgY2VsbHMgZG8gbm90IGhhdmUgaGVtb2dsb2JpbiBleHByZXNzaW9uIGFuZCBhcmUgaW4gZmFjdCBtaXMtbWFwcGVkOyB0aGV5IHdpbGwgYmUgZmxhZ2dlZCBieSB0aGUgUUMgZmlsdGVyIGFuZCBleGNsdWRlZCBmcm9tIGNlbGwgdHlwZSBhc3NpZ25tZW50cy4KCioqUGxlYXNlIGFkanVzdCB0aGUgTUFEX3RocmVzaG9sZCAodHlwaWNhbGx5IGJldHdlZW4gMSBhbmQgMykgYmFzZWQgb24gdGhlIGRpc3RyaWJ1dGlvbiBvZiB5b3VyIGRhdGFzZXQgdG8gaWRlbnRpZnkgdGhlIG91dGxpZXJzIHdpdGggbG93IHF1YWxpdHkgYW5kIGhpZ2ggbWFwcGluZyBlcnJvciBzY29yZXMuIFRoaXMgd2lsbCBpbXByb3ZlIHlvdXIgY2xhc3NpZmljYXRpb25zIGFuZCBhbnkgZG93bnN0cmVhbSBjb21wb3NpdGlvbiBhbmFseXNpcyoqCgoKYGBge3J9CiMgIyBPcHRpb25hbCBzdGVwIC0gcmVtb3ZlIG91dGxpZXJzIHdpdGggaGlnaCBtYXBwaW5nIGVycm9yCiMgcXVlcnkgPC0gc3Vic2V0KHF1ZXJ5LCBtYXBwaW5nX2Vycm9yX1FDID09ICdQYXNzJykKYGBgCgpPcHRpb25hbGx5LCBvdXRsaWVyIGNlbGxzIHdpdGggaGlnaCBtYXBwaW5nIGVycm9yIGNhbiBhbHNvIGJlIHJlbW92ZWQgYXQgdGhpcyBzdGFnZS4KRm9yIGVhc2Ugb2YgaW50ZWdyYXRpbmcgdGhlc2UgbWFwcGVkIGFubm90YXRpb25zIHdpdGggdGhlIHJlc3Qgb2YgeW91ciBhbmFseXNpcywgd2UgY2FuIGNob29zZSB0byBza2lwIHRoaXMgc3RlcC4gSWYgc28sIEZpbmFsIENlbGxUeXBlIGFuZCBQc2V1ZG90aW1lIHByZWRpY3Rpb25zIHdpbGwgYmUgYXNzaWduZWQgYXMgTkEgZm9yIGNlbGxzIGZhaWxpbmcgdGhlIG1hcHBpbmcgZXJyb3IgUUMgdGhyZXNob2xkLiAKCgojIyMgQ2VsbCBUeXBlIEFzc2lnbm1lbnRzCldlIHdpbGwgbmV4dCB1c2UgYSBLTk4gY2xhc3NpZmllciB0byBhc3NpZ24gY2VsbCBpZGVudGl0eSBiYXNlZCBvbiB0aGUgMzAgSy1OZWFyZXN0IE5laWdoYm91cnMgZnJvbSB0aGUgcmVmZXJlbmNlIG1hcC4KQnJvYWRlciBjZWxsIHR5cGUgbGFiZWxzIHdpbGwgYWxzbyBiZSB0cmFuc2ZlcnJlZCBhdXRvbWF0aWNhbGx5IGFsb25nIHdpdGggdGhlIHByZWNpc2UgY2VsbCB0eXBlIGxhYmVscy4gClRoaXMgbGFiZWwgdHJhbnNmZXIgc3RlcCBzaG91bGQgdGFrZSA8MSBtaW4gZm9yIDEwLDAwMCBjZWxscyBhbmQgPDUgbWluIGZvciAxMDAsMDAwIGNlbGxzLgoKYGBge3IsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTEwfQojIFByZWRpY3QgSGVtYXRvcG9pZXRpYyBDZWxsIFR5cGVzIGJ5IEtOTiBjbGFzc2lmaWNhdGlvbgpxdWVyeSA8LSBwcmVkaWN0X0NlbGxUeXBlcygKICBxdWVyeV9vYmogPSBxdWVyeSwgCiAgcmVmX29iaiA9IHJlZiwgCiAgaW5pdGlhbF9sYWJlbCA9ICdpbml0aWFsX0NlbGxUeXBlJywgIyBjZWxsdHlwZSBhc3NpZ25tZW50cyBiZWZvcmUgZmlsdGVyaW5nIG9uIG1hcHBpbmcgUUMKICBmaW5hbF9sYWJlbCA9ICdwcmVkaWN0ZWRfQ2VsbFR5cGUnICAjIGNlbGx0eXBlIGFzc2lnbm1lbnRzIHdpdGggbWFwIFFDIGZhaWxpbmcgY2VsbHMgYXNzaWduZWQgYXMgTkEKKSAKCkRpbVBsb3Qoc3Vic2V0KHF1ZXJ5LCBtYXBwaW5nX2Vycm9yX1FDID09ICdQYXNzJyksIHJlZHVjdGlvbiA9ICd1bWFwX3Byb2plY3RlZCcsIGdyb3VwLmJ5ID0gYygncHJlZGljdGVkX0NlbGxUeXBlJyksIAogICAgICAgIHJhc3Rlcj1GQUxTRSwgbGFiZWw9VFJVRSwgbGFiZWwuc2l6ZSA9IDQpCmBgYAoKV2UgY2FuIGFsc28gdmlzdWFsaXplIHRoZSBicm9hZGVyIGNlbGwgdHlwZSBjYXRlZ29yaWVzIGluIGNhc2UgcHJlY2lzZSBsYWJlbHMgYXJlIHRvbyBncmFudWxhci4gClRoZXNlIHByb3ZpZGUgYSBzaW1wbGVyIHZpZXcgb2YgdGhlIGRhdGEgYW5kIGNhbiBoZWxwIGd1aWRlIGNsdXN0ZXIgYW5ub3RhdGlvbnMgaWYgeW91ciBkYXRhc2V0IGRvZXMgbm90IGhhdmUgbWFueSBjZWxscy4gCgpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTB9CkRpbVBsb3Qoc3Vic2V0KHF1ZXJ5LCBtYXBwaW5nX2Vycm9yX1FDID09ICdQYXNzJyksIHJlZHVjdGlvbiA9ICd1bWFwX3Byb2plY3RlZCcsIGdyb3VwLmJ5ID0gYygncHJlZGljdGVkX0NlbGxUeXBlX0Jyb2FkJyksIAogICAgICAgIHJhc3Rlcj1GQUxTRSwgbGFiZWw9VFJVRSwgbGFiZWwuc2l6ZSA9IDQpCmBgYAoKCiMjIyMgUHNldWRvdGltZSBBbm5vdGF0aW9ucwpXZSBjYW4gYWxzbyBhbm5vdGF0ZSBlYWNoIHF1ZXJ5IGNlbGwgYmFzZWQgb24gdGhlaXIgcG9zaXRpb24gYWxvbmcgaGVtYXRvcG9pZXRpYyBwc2V1ZG90aW1lLiAKUXVlcnkgY2VsbHMgd2lsbCBiZSBhc3NpZ25lZCBhIHBzZXVkb3RpbWUgc2NvcmUgYmFzZWQgb24gdGhlIDMwIEstTmVhcmVzdCBOZWlnaGJvdXJzIGZyb20gdGhlIHJlZmVyZW5jZSBtYXAuClNpbmNlIG91ciBQc2V1ZG90aW1lIEtOTiBhc3NpZ25tZW50cyBhcmUgcGVyZm9ybWVkIGluIFVNQVAgc3BhY2UgKG1vcmUgYWNjdXJhdGUgdGhhbiBLTk4gb24gaGFybW9ueSBjb21wb25lbnRzKSwgdGhpcyBzdGVwIGlzIHZlcnkgZmFzdCAoPCAxMHMpCgpgYGB7ciwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9MTJ9CiMgUHJlZGljdCBQc2V1ZG90aW1lIHZhbHVlcyBieSBLTk4KcXVlcnkgPC0gcHJlZGljdF9Qc2V1ZG90aW1lKAogIHF1ZXJ5X29iaiA9IHF1ZXJ5LCAKICByZWZfb2JqID0gcmVmLCAKICBpbml0aWFsX2xhYmVsID0gJ2luaXRpYWxfUHNldWRvdGltZScsICAjIHBzZXVkb3RpbWUgYXNzaWdubWVudHMgYmVmb3JlIGZpbHRlcmluZyBvbiBtYXBwaW5nIFFDCiAgZmluYWxfbGFiZWwgPSAncHJlZGljdGVkX1BzZXVkb3RpbWUnICAgIyBwc2V1ZG90aW1lIGFzc2lnbm1lbnRzIHdpdGggbWFwIFFDIGZhaWxpbmcgY2VsbHMgYXNzaWduZWQgYXMgTkEKKQoKIyBWaXN1YWxpemUgSGVtYXRvcG9pZXRpYyBQc2V1ZG90aW1lIGluIHF1ZXJ5IGRhdGEKRmVhdHVyZVBsb3Qoc3Vic2V0KHF1ZXJ5LCBtYXBwaW5nX2Vycm9yX1FDID09ICdQYXNzJyksIGZlYXR1cmVzID0gYygncHJlZGljdGVkX1BzZXVkb3RpbWUnKSwgc3BsaXQuYnkgPSAnUGF0aWVudCcpICYgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IHJldihicmV3ZXIucGFsKDExLCAnUmRCdScpKSkKYGBgCgoKIyMjIFZpc3VhbGl6ZSBQcm9qZWN0aW9uIERlbnNpdHkKCk5vdyBsZXQncyB2aXN1YWxpemUgdGhlIGRlbnNpdHkgZGlzdHJpYnV0aW9uIG9mIHF1ZXJ5IGNlbGxzIGFjcm9zcyB0aGUgaGVtYXRvcG9pZXRpYyBoaWVyYXJjaHkKCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0xMn0KIyBTZXQgYmF0Y2gvY29uZGl0aW9uIHRvIGJlIHZpc3VhbGl6ZWQgaW5kaXZpZHVhbGx5CmJhdGNoX2tleSA8LSAnUGF0aWVudCcKCiMgcmV0dXJucyBhIGxpc3Qgb2YgcGxvdHMgZm9yIGVhY2ggZG9ub3IgZnJvbSBhIHByZS1zcGVjaWZpZWQgYmF0Y2ggdmFyaWFibGUKcHJvamVjdGlvbl9wbG90cyA8LSBwbG90X1Byb2plY3Rpb25fYnlEb25vcigKICBxdWVyeV9vYmogPSBxdWVyeSwgCiAgYmF0Y2hfa2V5ID0gYmF0Y2hfa2V5LCAKICByZWZfb2JqID0gcmVmLCAKICBIaWVyYXJjaHlfb25seSA9IEZBTFNFLCAjIFdoZXRoZXIgdG8gZXhjbHVkZSBUL05LL1BsYXNtYS9TdHJvbWFsIGNlbGxzIAogIGRvd25zYW1wbGVfcmVmZXJlbmNlID0gVFJVRSwgCiAgZG93bnNhbXBsZV9mcmFjID0gMC4yNSwgICAjIGRvd24tc2FtcGxlIHJlZmVyZW5jZSBjZWxscyB0byAyNSU7IHJlZHVjZXMgZmlndXJlIGZpbGUgc2l6ZQogIHF1ZXJ5X3BvaW50X3NpemUgPSAwLjIsICAgIyBhZGp1c3Qgc2l6ZSBvZiBxdWVyeSBjZWxscyBiYXNlZCBvbiAjIG9mIGNlbGxzCiAgc2F2ZXBsb3QgPSBUUlVFLCAKICBzYXZlX2ZvbGRlciA9ICdwcm9qZWN0aW9uRmlndXJlcy8nCikKCiMgc2hvdyBwbG90cyB0b2dldGhlciB3aXRoIHBhdGNod29yay4gQ2FuIGFsc28ganVzdCBjYWxsICJwcm9qZWN0aW9uX3Bsb3RzIiBvYmplY3QgdG8gZGlzcGxheSBvbmUtYnktb25lCnBhdGNod29yazo6d3JhcF9wbG90cyhwcm9qZWN0aW9uX3Bsb3RzLCBuY29sID0gMykKYGBgCgpXZSBjYW4gYWxzbyBzZXQgSGllcmFyY2h5X29ubHkgPSBUUlVFIHRvIHJlbW92ZSBUL05LL1BsYXNtYS9TdHJvbWFsIGNlbGxzIGFuZCBmb2N1cyBzb2xlbHkgb24gdGhlIGhlbWF0b3BvaWV0aWMgaGllcmFyY2h5LgoKYGBge3IsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTEwfQojIFNldCBiYXRjaC9jb25kaXRpb24gdG8gYmUgdmlzdWFsaXplZCBpbmRpdmlkdWFsbHkKYmF0Y2hfa2V5IDwtICdQYXRpZW50JwoKIyByZXR1cm5zIGEgbGlzdCBvZiBwbG90cyBmb3IgZWFjaCBkb25vciBmcm9tIGEgcHJlLXNwZWNpZmllZCBiYXRjaCB2YXJpYWJsZQpwcm9qZWN0aW9uX3Bsb3RzIDwtIHBsb3RfUHJvamVjdGlvbl9ieURvbm9yKAogIHF1ZXJ5X29iaiA9IHF1ZXJ5LCAKICBiYXRjaF9rZXkgPSBiYXRjaF9rZXksIAogIHJlZl9vYmogPSByZWYsIAogIEhpZXJhcmNoeV9vbmx5ID0gVFJVRSwgIyBXaGV0aGVyIHRvIGV4Y2x1ZGUgVC9OSy9QbGFzbWEvU3Ryb21hbCBjZWxscyAKICBkb3duc2FtcGxlX3JlZmVyZW5jZSA9IFRSVUUsIAogIGRvd25zYW1wbGVfZnJhYyA9IDAuMjUsICAgIyBkb3duLXNhbXBsZSByZWZlcmVuY2UgY2VsbHMgdG8gMjUlOyByZWR1Y2VzIGZpZ3VyZSBmaWxlIHNpemUKICBxdWVyeV9wb2ludF9zaXplID0gMC4yLCAgICMgYWRqdXN0IHNpemUgb2YgcXVlcnkgY2VsbHMgYmFzZWQgb24gIyBvZiBjZWxscwogIHNhdmVwbG90ID0gVFJVRSwgCiAgc2F2ZV9mb2xkZXIgPSAncHJvamVjdGlvbkZpZ3VyZXMvJwopCgojIHNob3cgcGxvdHMgdG9nZXRoZXIgd2l0aCBwYXRjaHdvcmsuIENhbiBhbHNvIGp1c3QgY2FsbCAicHJvamVjdGlvbl9wbG90cyIgb2JqZWN0IHRvIGRpc3BsYXkgb25lLWJ5LW9uZQpwYXRjaHdvcms6OndyYXBfcGxvdHMocHJvamVjdGlvbl9wbG90cywgbmNvbCA9IDMpCmBgYAoKCiMjIyBHZXQgQ29tcG9zaXRpb24gZGF0YSBmb3IgZWFjaCBkb25vcgoKSGVyZSwgdG8gc3R1ZHkgdGhlIGFidW5kYW5jZSBvZiBlYWNoIGNlbGwgdHlwZSB3aXRoaW4gZWFjaCBkb25vciwgSSBmb2N1cyBvbiBjZWxscyB0aGF0IHdlcmUgY2xhc3NpZmllZCB3aXRoIGEgS05OIHByb2IgPiAwLjUgKHRoYXQgaXMsID41MCUgb2YgbmVhcmVzdCBuZWlnaGJvdXJzIGZyb20gdGhlIHJlZmVyZW5jZSBtYXAgYWdyZWUgb24gdGhlIGFzc2lnbmVkIGNlbGwgdHlwZSkuIAoKV2UgY2FuIHByZXNlbnQgdGhpcyBhcyBhIGxvbmcgdGFibGUKCmBgYHtyfQpxdWVyeV9jb21wb3NpdGlvbiA8LSBnZXRfQ29tcG9zaXRpb24oCiAgcXVlcnlfb2JqID0gcXVlcnksIAogIGRvbm9yX2tleSA9ICdQYXRpZW50JywgCiAgY2VsbHR5cGVfbGFiZWwgPSAncHJlZGljdGVkX0NlbGxUeXBlJywgCiAgbWFwUUNfY29sID0gJ21hcHBpbmdfZXJyb3JfUUMnLCAKICBrbm5fcHJvYl9jdXRvZmYgPSAwLjUsIAogIHJldHVybl90eXBlID0gJ2xvbmcnKQoKcXVlcnlfY29tcG9zaXRpb24gCmBgYAoKT3IgYXMgYSB3aWRlIHRhYmxlIHdpdGggY291bnRzIG9mICMgb2YgY2VsbHMKCmBgYHtyfQpxdWVyeV9jb21wb3NpdGlvbiA8LSBnZXRfQ29tcG9zaXRpb24oCiAgcXVlcnlfb2JqID0gcXVlcnksIAogIGRvbm9yX2tleSA9ICdQYXRpZW50JywgCiAgY2VsbHR5cGVfbGFiZWwgPSAncHJlZGljdGVkX0NlbGxUeXBlJywgCiAgbWFwUUNfY29sID0gJ21hcHBpbmdfZXJyb3JfUUMnLCAKICBrbm5fcHJvYl9jdXRvZmYgPSAwLjUsIAogIHJldHVybl90eXBlID0gJ2NvdW50JykKCnF1ZXJ5X2NvbXBvc2l0aW9uIApgYGAKCk9yIGFzIGEgd2lkZSB0YWJsZSB3aXRoIHByb3BvcnRpb24gb2YgZWFjaCBjZWxsIHR5cGUgd2l0aGluIGVhY2ggZG9ub3IKCmBgYHtyfQpxdWVyeV9jb21wb3NpdGlvbiA8LSBnZXRfQ29tcG9zaXRpb24oCiAgcXVlcnlfb2JqID0gcXVlcnksIAogIGRvbm9yX2tleSA9ICdQYXRpZW50JywgCiAgY2VsbHR5cGVfbGFiZWwgPSAncHJlZGljdGVkX0NlbGxUeXBlJywgCiAgbWFwUUNfY29sID0gJ21hcHBpbmdfZXJyb3JfUUMnLCAKICBrbm5fcHJvYl9jdXRvZmYgPSAwLjUsIAogIHJldHVybl90eXBlID0gJ3Byb3BvcnRpb24nKQoKcXVlcnlfY29tcG9zaXRpb24gCmBgYAoKCmBgYHtyfQojIFNpbXBsZSBoZWF0bWFwIHRvIHZpc3VhbGl6ZSBjb21wb3NpdGlvbiBvZiBwcm9qZWN0ZWQgc2FtcGxlcwpwIDwtIHF1ZXJ5X2NvbXBvc2l0aW9uICU+JSAKICAjIHNob3cgY2VsbHR5cGVzIHByZXNlbnQgaW4gPjElIG9mIHRvdGFsIGNlbGxzCiAgc2VsZWN0KFBhdGllbnQsIGNvbG5hbWVzKHF1ZXJ5X2NvbXBvc2l0aW9uKVstMV1bY29sU3VtcyhxdWVyeV9jb21wb3NpdGlvblstMV0pID4gMC4wMV0pICU+JSAKICAjIGNvbnZlcnQgdG8gbWF0cml4IGFuZCBkaXNwbGF5IGhlYXRtYXAKICBjb2x1bW5fdG9fcm93bmFtZXMoJ1BhdGllbnQnKSAlPiUgZGF0YS5tYXRyaXgoKSAlPiUgQ29tcGxleEhlYXRtYXA6OkhlYXRtYXAoKQpwCmBgYAoKCiMjIyBFeHRyYTogU2NvcmUgQU1MIGNlbGxzIGZvciBlbnJpY2htZW50IG9mIExTQyBzaWduYXR1cmVzCgpIZXJlIHdlIHdpbGwgbG9hZCBnZW5lc2V0cyBkZXJpdmVkIGZyb20gZnVuY3Rpb25hbCBzdHVkaWVzIG9mIExTQysgYW5kIExTQy0gZnJhY3Rpb25zIGluIGRpdmVyc2UgQU1MIHBhdGllbnRzIChOZyBOYXR1cmUgMjAxNyksIGFuZCBMU0MrIHZzIExTQy0gZnJhY3Rpb25zIGluIHBhdGllbnRzIHdpdGggTUxMIHRyYW5zbG9jYXRpb25zIChTb21lcnZhaWxsZSBDZWxsIFN0ZW0gQ2VsbCAyMDA5KS4gVGhlIGZvcm1lciB0ZW5kcyB0byBjb3JyZXNwb25kIHRvIG1vcmUgcHJpbWl0aXZlIExTQ3Mgd2hpbGUgdGhlIGxhdHRlciBjb3JyZXNwb25kcyB0byBhIGRpc3RpbmN0IHNpZ25hdHVyZSBvZiBtYXR1cmUgcHJvbW9ub2N5dGljIExTQ3MuCgpGdXJ0aGVyLCB3ZSB3aWxsIGxvYWQgYWRkaXRpb25hbCBnZW5lc2V0cyBkZXJpdmVkIGZyb20gbWFya2VyIGdlbmVzIG9mIGxldWtlbWlhIGNlbGwgcG9wdWxhdGlvbnMgZGVmaW5lZCBpbiB2YW4gR2FsZW4gZXQgYWwgQ2VsbCAyMDE5IGFuZCBaZW5nIGV0IGFsIE5hdHVyZSBNZWRpY2luZSAyMDIyLiBOb3RhYmx5LCB0aGUgTFNQQy1RdWllc2NlbnQgcG9wdWxhdGlvbiBpcyBoaWdobHkgYXNzb2NpYXRlZCB3aXRoIGZ1bmN0aW9uYWwgTFNDcyBhbmQgcmVsYXBzZSBieSBkZWNvbnZvbHV0aW9uIChaZW5nIDIwMjIpLgoKYGBge3J9CmN1cmw6OmN1cmxfZG93bmxvYWQoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9hbmR5Z3h6ZW5nL0JvbmVNYXJyb3dNYXBfRXh0cmFzL21haW4vc2NBTUxfRGlmZmVyZW50aWF0aW9uX1N0YWdlX01hcmtlckdlbmVzLmdtdCcsIAogICAgICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gcGFzdGUwKHByb2plY3Rpb25fcGF0aCwgJ3NjQU1MX0RpZmZlcmVudGlhdGlvbl9TdGFnZV9NYXJrZXJHZW5lcy5nbXQnKSkKQU1MZ2VuZXNldHMgPC0gbG9hZF9HZW5lc2V0c19nbXQoJ3NjQU1MX0RpZmZlcmVudGlhdGlvbl9TdGFnZV9NYXJrZXJHZW5lcy5nbXQnKQpBTUxnZW5lc2V0cyAlPiUgc3VtbWFyeSgpCmBgYAoKYGBge3J9CiMgc2NvcmUgZ2VuZXNldHMgYnkgQVVDZWxsIGFuZCBhZGQgdG8gbWV0YWRhdGEKIyB0eXBpY2FsbHkgSSBzcGxpdCB0aGUgZGF0YSBpbnRvIGJhdGNoZXMgb2YgNWstMTBrIGNlbGxzIHRvIGNvbnNlcnZlIG1lbW9yeQpxdWVyeSA8LSBzY29yZV9HZW5lc2V0c19BVUNlbGwocXVlcnksIGdlbmVzZXRzID0gQU1MZ2VuZXNldHMsIG5iYXRjaGVzID0gMSwgbmNvcmVzID0gMTAsIG91dHB1dCA9ICdtZXRhZGF0YScpCnF1ZXJ5QG1ldGEuZGF0YQpgYGAKClVzaW5nIGdlbmVzIGFzc29jaWF0ZWQgd2l0aCBmdW5jdGlvbmFsIExTQysgZW5ncmFmdG1lbnQgY2FwYWNpdHkgKExTQ192c19CbGFzdF9VUF9fTmcyMDE2X0FVQykgYWxvbmcgd2l0aCB0aGUgTFNQQy1RdWllc2NlbnQgc2lnbmF0dXJlIGhlbHAgdXMgdG8gaWRlbnRpZnkgY2FuZGlkYXRlIExTQ3Mgd2l0aGluIHRoZSBkYXRhLiAKCmBgYHtyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMn0KRmVhdHVyZVBsb3Qoc3Vic2V0KHF1ZXJ5LCBtYXBwaW5nX2Vycm9yX1FDID09ICdQYXNzJyksIAogICAgICAgICAgICBmZWF0dXJlcyA9IGMoJ0xTUENfUXVpZXNjZW50X19aZW5nMjAyMl9BVUMnLCAnTFNDX3ZzX0JsYXN0X1VQX19OZzIwMTZfQVVDJyksIAogICAgICAgICAgICBzcGxpdC5ieSA9ICdQYXRpZW50JywgbWF4LmN1dG9mZiA9ICdxOTknLCBtaW4uY3V0b2ZmID0gJ3E1JykgJiAKICBzY2FsZV9jb2xvcl9ncmFkaWVudG4oY29sb3JzID0gcmV2KGJyZXdlci5wYWwoMTEsICdSZEJ1JykpKQpgYGAKCkl0IGlzIG5vIHN1cnByaXNlIHRoYXQgdGhlc2UgYXJlIHRoZSBtb3N0IHByaW1pdGl2ZSBjZWxscyBlYXJsaWVzdCBpbiBQc2V1ZG90aW1lLiBOb3RlIHRoYXQgdGhpcyBhcHByb2FjaCBkb2VzIG5vdCB3b3JrIGZvciB0aGUgTUxMLUFGOSBzYW1wbGVzIHdoZXJlaW4gYWxsIGxldWtlbWljIGNlbGxzIGFyZSBtYXR1cmUgbXllbG9pZDsgaW4gdGhpcyBjb250ZXh0IHRoZSBNTExfTFNDX1NvbWVydmFpbGxlXzIwMDlfVVAgc2lnbmF0dXJlIG1heSBoZWxwIHVzIGlkZW50aWZ5IGZ1bmN0aW9uYWwgIkxTQ3MiIGNhcGFibGUgb2YgZW5ncmFmdG1lbnQgd2l0aGluIHRoZSBtYXR1cmUgbXllbG9pZCBwb3B1bGF0aW9ucyAodHlwaWNhbGx5IGxhdGUgR01QcyBvciBFYXJseSBQcm9Nb25vY3l0ZXMpLiAKCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0xMn0KRmVhdHVyZVBsb3Qoc3Vic2V0KHF1ZXJ5LCBtYXBwaW5nX2Vycm9yX1FDID09ICdQYXNzJyksIAogICAgICAgICAgICBmZWF0dXJlcyA9IGMoJ01MTF9MU0NfVVBfX1NvbWVydmFpbGxlMjAwOV9BVUMnKSwgCiAgICAgICAgICAgIHNwbGl0LmJ5ID0gJ1BhdGllbnQnLCBtYXguY3V0b2ZmID0gJ3E5OScsIG1pbi5jdXRvZmYgPSAncTUnKSAmIAogIHNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSByZXYoYnJld2VyLnBhbCgxMSwgJ1JkQnUnKSkpCmBgYAoKQXMgZXhwZWN0ZWQsIFByb01vbm9jeXRlcyB3aXRoaW4gdGhlIE1MTC1BRjkgc2FtcGxlIGV4aGliaXQgZW5yaWNobWVudCBmb3IgdGhlIE1MTF9MU0NfU29tZXJ2YWlsbGVfMjAwOV9VUCwgaW4gbGluZSB3aXRoIHRoZSBtYXR1cmUgbXllbG9pZCBvcmlnaW4gb2YgdGhpcyBwYXRpZW50J3MgZGlzZWFzZS4KCgojIyMgU2F2ZSBwcm9qZWN0aW9uIHJlc3VsdHMKClRoaXMgd2lsbCBzYXZlIGEgY3N2IGZpbGUgd2l0aCB0aGUgbWFwcGVkIGFubm90YXRpb25zIGZvciBlYWNoIGNlbGwgKG1hcHBpbmcgZXJyb3Igc2NvcmVzLCB1bWFwIGNvb3JkaW5hdGVzLCBwcmVkaWN0ZWQgQ2VsbCBUeXBlLCBhbmQgcHJlZGljdGVkIFBzZXVkb3RpbWUpLiBXZSBhbHNvIGhhdmUgdGhlIG9wdGlvbiB0byBzYXZlIEFVQ2VsbCBzY29yZXMsIHByb3ZpZGVkIHRoYXQgdGhleSBhcmUgaW4gdGhlIG1ldGFkYXRhLiAKCmBgYHtyfQojIFNhdmUgQ2VsbFR5cGUgQW5ub3RhdGlvbnMgYW5kIFByb2plY3RlZCBVTUFQIGNvb3JkaW5hdGVzCnNhdmVfUHJvamVjdGlvblJlc3VsdHMoCiAgcXVlcnlfb2JqID0gcXVlcnksIAogIGNlbGx0eXBlX2xhYmVsID0gJ3ByZWRpY3RlZF9DZWxsVHlwZScsIAogIGNlbGx0eXBlX0tOTnByb2JfbGFiZWwgPSAncHJlZGljdGVkX0NlbGxUeXBlX3Byb2InLCAKICBwc2V1ZG90aW1lX2xhYmVsID0gJ3ByZWRpY3RlZF9Qc2V1ZG90aW1lJywgCiAgc2F2ZV9BVUNlbGxfc2NvcmVzID0gVFJVRSwKICBmaWxlX25hbWUgPSAncXVlcnlkYXRhX3Byb2plY3RlZF9sYWJlbGVkLmNzdicpCmBgYAoKCiMjIE5vdGUgb24gRG93bnN0cmVhbSBBbmFseXNpczogCgpGb3IgZG93bnN0cmVhbSBhbmFseXNpcywgeW91IGNhbiB1c2UgdGhlIHByb2plY3RlZCBjZWxsdHlwZSBsYWJlbHMgdG8gaGVscCBhbm5vdGF0ZSBhbnkgbGV1a2VtaWEgY2VsbCBjbHVzdGVycyBnZW5lcmF0ZWQgdGhyb3VnaCB1bnN1cGVydmlzZWQgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIGFuZCBjbHVzdGVyaW5nIGZyb20gaW5kaXZpZHVhbCBwYXRpZW50cy4gU29tZXRpbWVzLCB1bnN1cGVydmlzZWQgYW5hbHlzaXMgd2lsbCB5aWVsZCBjbHVzdGVycyBjb3JyZXNwb25kaW5nIHRvIGRpZmZlcmVudCBkZXZlbG9wbWVudGFsIHN0YXRlcyBhbmQgb3RoZXIgdGltZXMgaXQgbWF5IHlpZWxkIGNsdXN0ZXJzIGNvcnJlc3BvbmRpbmcgdG8gZGlzdGluY3Qgc3ViY2xvbmVzIHdpdGhpbiB0aGUgcGF0aWVudC4gQWxvbmcgd2l0aCB0b29scyBsaWtlIGluZmVyQ05WIGZvciBwYXRpZW50cyB3aXRoIGtub3duIGN5dG9nZW5ldGljIGFibm9ybWFsaXRpZXMsIHRoaXMgY2FuIGJlIGludGVncmF0ZWQgdG8gdmlzdWFsaXplIGNlbGx1bGFyIGhpZXJhcmNoaWVzIGF0IHRoZSBsZXZlbCBvZiBpbmRpdmlkdWFsIHN1YmNsb25lcy4gCgpBZGRpdGlvbmFsIGluZm9ybWF0aW9uIGZyb20gcHNldWRvdGltZSBwcm9qZWN0aW9uIGFuZCBzY29yaW5nIG9mIExTQy1zcGVjaWZpYyBzaWduYXR1cmVzIGNhbiBoZWxwIGlkZW50aWZ5IGNhbmRpZGF0ZSBMU0NzIHdpdGhpbiB0aGUgZGF0YS4gCgpGb3IgZG93bnN0cmVhbSBhbmFseXNpcywgbGV0J3MgdGFrZSB0aGUgZXhhbXBsZSBvZiBwdDMwODg2LCB3aXRoIGludm9sdmVtZW50IG9mIEhTQ3MgYW5kIG11bHRpcGxlIHRyYWplY3RvcmllcyBzcGFubmluZyBNeWVsb2lkLCBFcnl0aHJvaWQsIGFuZCBwREMgZGlmZmVyZW50aWF0aW9uOgoKYGBge3IsIGZpZy5oZWlnaHQ9MywgZmlnLndpZHRoPTEyfQpwdDMwODg2IDwtIHF1ZXJ5ICU+JQogIHN1YnNldCguLCBQYXRpZW50ID09ICdwdF8zMDg4NicpICU+JSAKICBTQ1RyYW5zZm9ybSgpICU+JSAKICBSdW5QQ0EoKSAlPiUgCiAgUnVuVU1BUChyZWR1Y3Rpb24gPSAncGNhJywgZGltcz0xOjIwKSAlPiUKICBGaW5kTmVpZ2hib3JzKHJlZHVjdGlvbiA9ICdwY2EnLCBkaW1zID0gMToyMCkgJT4lIAogIEZpbmRDbHVzdGVycyhyZXNvbHV0aW9uID0gMSwgYWxnb3JpdGhtID0gMykKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9MTJ9CkRpbVBsb3QocHQzMDg4NiwgcmVkdWN0aW9uID0gJ3VtYXAnLCBncm91cC5ieT1jKCdzZXVyYXRfY2x1c3RlcnMnLCAncHJlZGljdGVkX0NlbGxUeXBlX0Jyb2FkJyksIGxhYmVsID0gVCkKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9MTN9CkZlYXR1cmVQbG90KHB0MzA4ODYsIAogICAgICAgICAgICBmZWF0dXJlcyA9IGMoJ0xTUENfUXVpZXNjZW50X19aZW5nMjAyMl9BVUMnLCAnTFNDX3ZzX0JsYXN0X1VQX19OZzIwMTZfQVVDJywgJ0hTQ19NUFBfbGlrZV9fVG9wMTAwX3NjQU1MX0FVQycpLCAKICAgICAgICAgICAgbWF4LmN1dG9mZiA9ICdxOTknLCBtaW4uY3V0b2ZmID0gJ3E1JywgbmNvbCA9IDMpICYgCiAgc2NhbGVfY29sb3JfZ3JhZGllbnRuKGNvbG9ycyA9IHJldihicmV3ZXIucGFsKDExLCAnUmRCdScpKSkKYGBgCgpCYXNlZCBvbiB0aGlzIHdlIGNhbiBzZWUgbXVsdGlwbGUgdHJhbnNjcmlwdGlvbmFsIGNsdXN0ZXJzIHdpdGhpbiB0aGUgZGF0YSwgaW5jbHVkaW5nIHNvbWUgbm9uLWxldWtlbWljIGNsdXN0ZXJzIG9mIFQgY2VsbHMsIEIgY2VsbHMsIGFuZCBQbGFzbWEgY2VsbHMuIApDbHVzdGVyIDMgY29udGFpbnMgSFNDL01QUC1saWtlIGNlbGxzIG1vc3QgZW5yaWNoZWQgZm9yIExTQyBzaWduYXR1cmVzLiAKCkxldCdzIHNlZSB3aGF0IGVhY2ggY2x1c3RlciBsb29rcyBsaWtlIHdoZW4gcHJvamVjdGVkIG9udG8gQm9uZU1hcnJvd01hcDoKCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD05fQpEaW1QbG90KHB0MzA4ODYsIHJlZHVjdGlvbiA9ICd1bWFwX3Byb2plY3RlZCcsIGdyb3VwLmJ5PWMoJ3NldXJhdF9jbHVzdGVycycsICdwcmVkaWN0ZWRfQ2VsbFR5cGVfQnJvYWQnKSwgbGFiZWwgPSBUKQpgYGAKCmBgYHtyfQpwdDMwODg2QG1ldGEuZGF0YSAlPiUgCiAgIyBjb21wYXJlIGNsdXN0ZXJzIGFnYWluc3QgYnJvYWQgY2VsbCB0eXBlcyBhc3NpZ25tZW50cyAKICBncm91cF9ieShwcmVkaWN0ZWRfQ2VsbFR5cGVfQnJvYWQsIHNldXJhdF9jbHVzdGVycykgJT4lIAogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lIHVuZ3JvdXAoKSAlPiUgCiAgIyBnZXQgcHJvcG9ydGlvbnMgb2YgY2VsbHMgd2l0aGluIGVhY2ggY2x1c3RlciBhc3NpZ25lZCBhcyBhIGNlcnRhaW4gY2VsbHR5cGUgCiAgZ3JvdXBfYnkoc2V1cmF0X2NsdXN0ZXJzKSAlPiUgbXV0YXRlKHByb3AgPSBjb3VudCAvIHN1bShjb3VudCkpICU+JSAKICBwaXZvdF93aWRlcihpZF9jb2xzID0gJ3ByZWRpY3RlZF9DZWxsVHlwZV9Ccm9hZCcsIG5hbWVzX2Zyb20gPSAnc2V1cmF0X2NsdXN0ZXJzJywgdmFsdWVzX2Zyb20gPSAncHJvcCcpICU+JSAKICAjIGZpbGwgaW4gemVyb3MgYW5kIGZvcm1hdCBmb3IgaGVhdG1hcCAKICByZXBsYWNlKGlzLm5hKC4pLCAwKSAlPiUgY29sdW1uX3RvX3Jvd25hbWVzKCdwcmVkaWN0ZWRfQ2VsbFR5cGVfQnJvYWQnKSAlPiUgCiAgIyBjcmVhdGUgYSBiYXNpYyBoZWF0bWFwIHRvIHZpc3VhbGl6ZSBjZWxsIHR5cGVzIHZzIGNsdXN0ZXJzCiAgcGhlYXRtYXA6OnBoZWF0bWFwKCkKYGBgCgpUaGlzIGlsbHVzdHJhdGVzIGhvdyBhbm5vdGF0aW9ucyBmcm9tIEJvbmVNYXJyb3dNYXAgY2FuIGFsbG93IHVzIHRvIGFuYWx5emUgY2x1c3RlcnMgb2YgbGV1a2VtaWMgY2VsbHMgaWRlbnRpZmllZCBmcm9tIHVuc3VwZXJ2aXNlZCBhbmFseXNpcy4gCkFzIHNob3duIGluIHRoZSBoZWF0bWFwIGFib3ZlLCBlYWNoIGNsdXN0ZXIgZnJvbSB0aGlzIHBhdGllbnQgYWN0dWFsbHkgY29ycmVzcG9uZHMgdG8gYSBkaXN0aW5jdCBzdGF0ZSBhbG9uZyB0aGUgaGVtYXRvcG9pZXRpYyBoaWVyYXJjaHksIHdpdGggbXVsdGlwbGUgY2x1c3RlcnMgY29ycmVzcG9uZGluZyB0byBlcnl0aHJvaWQgcHJvZ2VuaXRvcnMuIE92ZXJsYXlpbmcgY29weSBudW1iZXIgYWx0ZXJhdGlvbiBjYWxscyBmcm9tIGluZmVyQ05WIG9yIGV4cHJlc3NlZCBtdXRhdGlvbnMgY2FuIGhlbHAgdXMgaWRlbnRpZnkgdGhlIGRpZmZlcmVudGlhdGlvbiBwYXR0ZXJucyBvZiBkaXN0aW5jdCBzdWJjbG9uZXMgd2l0aGluIHRoaXMgcGF0aWVudC4gCgoKRmluYWxseSwgZm9yIGNvaG9ydHMgd2l0aCBtYW55IHBhdGllbnRzIGNvbXBvc2l0aW9uIGFuYWx5c2lzIGNhbiBiZSBwZXJmb3JtZWQgdG8gdW5kZXJzdGFuZCBob3cgbGV1a2VtaWEgY2VsbCBoaWVyYXJjaGllcyB2YXJ5IHdpdGggcGF0aWVudCBjaGFyYWN0ZXJpc3RpY3MgYW5kIHRoZXJhcHkgcmVzcG9uc2UgLyByZWxhcHNlLiBJIGhvcGUgdGhpcyB0dXRvcmlhbCB3YXMgdXNlZnVsIGFuZCBmZWVsIGZyZWUgdG8gY29tbWVudCB3aXRoIGFueSBxdWVzdGlvbnMgeW91IG1heSBoYXZlIGFyb3VuZCBwcm9qZWN0aW9uIG9mIGxldWtlbWlhIGNlbGxzIG9yIGRvd25zdHJlYW0gYW5hbHlzaXMuCg==